package com.jason.canvas.view;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.CornerPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.PointF;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.view.View;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import static android.R.attr.bitmap;
import static android.R.attr.y;

/**
 * @DESC:
 * @Author: Jason
 * @Date: 16/9/20
 * @Time: 23:10
 */

public class PolylineView12 extends View {

    private static final float LEFT = 1 / 16F, TOP = 1 / 16F,
            RIGHT = 15 / 16F, BOTTOM = 7 / 8F; // 网格区域相对位置

    private static final float TIME_X = 3 / 32F, TIME_Y = 1 / 16F,
            MONEY_X = 31 / 32F, MONEY_Y = 15 / 16F; // 文字坐标相对位置

    private static final float TEXT_SIGN =  1 / 32F; // 文字相对大小
    private static final float THICK_LINE_WIDTH = 1 / 128F,
            THIN_LINE_WIDTH = 1 / 512F; //粗线和细线相对大小

    private TextPaint mTextPaint; // 文字画笔
    private Paint linePaint, pointPaint; // 线条画笔和点画笔
    private Path mPath; // 路径对象
    private Bitmap mBitmap; // 绘制曲线的Bitmap对象
    private Canvas mCanvas; // 装载Bitmap的Canvas对象

    private List<PointF> pointFs; // 数据列表
    private float[] rulerX, rulerY; // x y轴向刻度

    private String signX, signY; // 设置X和Y坐标分别表示什么的文字
    private float textY_X, textY_Y, textX_X, textX_Y; // 文字坐标
    private float textSignSize; // xy坐标标识文字体大小
    private float thickLineWidth, thinLineWidth; // 粗线和细线的宽度
    private float left, top, right,bottom; // 网格区域左上右下两点坐标
    private int viewSize; // 控件尺寸
    private float maxX, maxY; // 横纵轴向最大刻度
    private float spaceX, spaceY ;// 刻度间隔



    public PolylineView12(Context context, AttributeSet attrs) {
        super(context, attrs);

        // 实例化文本画笔 并设置参数
        mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG | Paint.LINEAR_TEXT_FLAG);
        mTextPaint.setColor(Color.WHITE);

        // 实例化线条画笔并设置参数
        linePaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
        linePaint.setStyle(Paint.Style.FILL);
        linePaint.setColor(Color.WHITE);

        // 实例化点画笔并设置参数
        pointPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
        pointPaint.setStyle(Paint.Style.FILL);
        pointPaint.setColor(Color.WHITE);

        // 实例化Path对象
        mPath = new Path();

        // 实例化canvas对象
        mCanvas = new Canvas();
        // 初始化数据
        initData();
    }

    /**
     * 初始化数据支撑
     * View初始化时可以考虑给予一个模拟器
     * 也可以通过setData方法设置自己的数据
     */
    private void initData() {
        Random random = new Random();
        pointFs = new ArrayList<>();
        for (int i = 0; i < 20; i++) {
            PointF pointF = new PointF();
            pointF.x = (float) (random.nextInt(100) * i);
            pointF.y = (float) (random.nextInt(100) * i);
            pointFs.add(pointF);
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {

        // 获取控件尺寸
        viewSize = w;

        // 计算纵轴标识文本坐标
        textY_X = viewSize * TIME_X;
        textY_Y = viewSize * TIME_Y;

        // 计算横轴标识文本坐标
        textX_X = viewSize * MONEY_X;
        textX_Y = viewSize * MONEY_Y;

        // 计算xy轴标识文本大小
        textSignSize = viewSize * TEXT_SIGN;

        // 计算网格左上右下两点坐标
        left = viewSize * LEFT;
        top = viewSize * TOP;
        right = viewSize * RIGHT;
        bottom = viewSize * BOTTOM;

        // 计算粗线宽度
        thickLineWidth = viewSize * THICK_LINE_WIDTH;

        // 计算细线宽度
        thinLineWidth = viewSize * THIN_LINE_WIDTH;

    }

    @Override
    protected void onDraw(Canvas canvas) {
        // 填充背景
        canvas.drawColor(0xFF9596C4);

        // 绘制标识元素
        drawSign(canvas);

        // 绘制网格
        drawGrid(canvas);

        // 绘制曲线
        drawPolyline(canvas);

    }

    /**
     * 绘制曲线
     * 使用一个新的Bitmap对象结合新的Canvas对象来绘制曲线
     * @param canvas
     */
    private void drawPolyline(Canvas canvas) {

        // 生成一个Bitmap对象大小和我们的网格大小一致
        mBitmap = Bitmap.createBitmap((int) (viewSize * (RIGHT - LEFT) - spaceX), (int) (viewSize * (BOTTOM - TOP) - spaceX), Bitmap.Config.ARGB_8888);

        // 将Bitmap注入Canvas
        mCanvas.setBitmap(mBitmap);

        // 为画布填充一个半透明的红色
        mCanvas.drawARGB(75, 255, 0, 0);

        // 重置曲线
        mPath.reset();

        // 生成Path和绘制Point
        for (int i = 0; i< pointFs.size(); i++) {
            // 计算x坐标
            float x = mCanvas.getWidth() / maxX * pointFs.get(i).x;

            // 计算y坐标
            float y = mCanvas.getHeight() / maxY * pointFs.get(i).y;
            y = mCanvas.getHeight() - y;

            // 绘制小点
            mCanvas.drawCircle(x, y, thickLineWidth, pointPaint);

            // 如果是第一个点 则将其设置为Path的起点
            if (i == 0) {
                mPath.moveTo(x, y);
            }

            // 连接各点
            mPath.lineTo(x, y);
        }

        // 设置PathEffect
        //linePaint.setPathEffect(new CornerPathEffect(200));

        // 重置线条宽度
        linePaint.setStrokeWidth(thickLineWidth);

        // 将Path绘制到自定义的Canvas上
        mCanvas.drawPath(mPath, linePaint);
        // 将mBitmap绘制到原来的canvas
        canvas.drawBitmap(mBitmap, left, top + spaceY, null);
    }

    /**
     * 绘制网格
     * @param canvas
     */
    private void drawGrid(Canvas canvas) {
        // 锁定画布
        canvas.save();

        // 设置线条画笔宽度
        linePaint.setStrokeWidth(thickLineWidth);

        // 计算xy轴Path
        mPath.moveTo(left, top);
        mPath.lineTo(left, bottom);
        mPath.lineTo(right, bottom);

        // 绘制xy轴
        canvas.drawPath(mPath, linePaint);

        // 绘制线条
        drawLines(canvas);

        // 释放画布
        canvas.restore();
    }

    /**
     * 绘制标识元素
     * @param canvas
     */
    private void drawSign(Canvas canvas) {
        // 锁定画布
        canvas.save();
        // 设置文本画笔文字尺寸
        mTextPaint.setTextSize(textSignSize);

        // 绘制纵轴标识文字
        mTextPaint.setTextAlign(Paint.Align.LEFT);
        canvas.drawText(null == signY ? "y" : signX, textY_X, textY_Y, mTextPaint);

        // 绘制横轴标识文字
        mTextPaint.setTextAlign(Paint.Align.RIGHT);
        canvas.drawText(null == signX ? "x" : signX, textX_X, textX_Y, mTextPaint);

        // 释放画布
        canvas.restore();

    }

    /**
     * 绘制网格
     * @param canvas
     */
    private void drawLines(Canvas canvas) {
        // 计算刻度文字尺寸
        float textRulerSize = textSignSize / 2F;

        // 重置文字画笔文字尺寸
        mTextPaint.setTextSize(textRulerSize);

        // 重置线条画笔描边宽度
        linePaint.setStrokeWidth(thinLineWidth);

        // 获取数据长度
        int count = pointFs.size();

        // 计算除数的值为数据长度减一
        int divisor = count - 1;

        // 计算横轴数据最大值
        maxX = 0;
        for (int i = 0; i < count; i++) {
            if (maxX < pointFs.get(i).x) {
                maxX = pointFs.get(i).x;
            }
        }

        // 计算横轴最近的能被count整除的值
        int remaindeX = ((int) maxX) % divisor;
        maxX = remaindeX == 0 ? ((int) maxX) : divisor - remaindeX + ((int) maxX);

        // 计算纵轴数据最大值
        maxY = 0;
        for (int i = 0; i<count; i++) {
            if (maxY < pointFs.get(i).y) {
                maxY = pointFs.get(i).y;
            }
        }

        // 计算纵轴最近的能被count整除的值
        int remainderY = ((int) maxY) % divisor;
        maxY = remainderY == 0 ? ((int) maxY) : divisor - remainderY + ((int) maxY);

        // 生成横轴刻度值
        rulerX = new float[count];
        for (int i = 0; i < count; i++) {
            rulerX[i] = maxX / divisor * i;
        }

        // 生成纵轴刻度值
        rulerY = new float[count];
        for (int i = 0; i < count; i++) {
            rulerY[i] = maxY / divisor * i;
        }

        // 计算横纵坐标刻度间隔
        spaceY = viewSize * (BOTTOM - TOP) / count;
        spaceX = viewSize * (RIGHT - LEFT) / count;

        // 锁定画布并设置画布透明度为75%
        int sc = canvas.saveLayerAlpha(0, 0, canvas.getWidth(), canvas.getHeight(), 75, Canvas.ALL_SAVE_FLAG);

        // 绘制横纵线段
        for (float y = viewSize * BOTTOM - spaceX; y > viewSize * TOP;y -= spaceY) {

            for (float x = viewSize * LEFT; x < viewSize * RIGHT; x += spaceX) {
                // 绘制纵向线段
                if (y == viewSize * TOP + spaceY) {
                    canvas.drawLine(x, y, x, y + spaceY * (count - 1), linePaint);
                }

                // 绘制横向线段
                if (x == viewSize * RIGHT - spaceX) {
                    canvas.drawLine(x, y, x - spaceX * (count - 1), y, linePaint);
                }
            }
        }


        // 还原画布
        canvas.restoreToCount(sc);

        // 绘制横纵轴向刻度值
        int index_x = 0, index_y = 1;
        for (float y = viewSize * BOTTOM - spaceY; y > viewSize * TOP; y -= spaceY) {
            for (float x = viewSize * LEFT; x < viewSize * RIGHT; x += spaceX) {
                // 绘制横轴刻度数值
                if (y == viewSize * BOTTOM - spaceY) {
                    canvas.drawText(String.valueOf(rulerX[index_x]), x, y + textSignSize + spaceY, mTextPaint);
                }

                // 绘制纵轴刻度数值
                if (x == viewSize * LEFT) {
                    canvas.drawText(String.valueOf(rulerY[index_y]), x - thickLineWidth, y + textRulerSize, mTextPaint);
                }

                index_x++;
            }

            index_y++;
        }

    }

    /**
     * 设置数据
     * @param pointFs 点集合
     * @param signX
     * @param signY
     */
    public synchronized void setData(List<PointF> pointFs, String signX, String signY) {
        if (null == pointFs || pointFs.size() == 0) {
            throw new IllegalArgumentException("No data to display !");
        }

        // 控制数据长度不超过10个 对于折线图来说数据太多就没有必要用折线图展示 而是使用散点图
        if (pointFs.size() > 10) {
            throw new IllegalArgumentException("The data is too long to display !");
        }

        // 设置数据并重绘视图
        this.pointFs = pointFs;
        this.signX = signX;
        this.signY = signY;
        invalidate();
    }
}
